package com.blog.wu.myblog.shiro;

import com.blog.wu.myblog.base.BaseService;
import com.blog.wu.myblog.shiro.stateless.JwtFilter;
import com.blog.wu.myblog.shiro.stateless.JwtRealm;
import com.jagregory.shiro.freemarker.ShiroTags;
import freemarker.template.TemplateException;
import net.sf.ehcache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.mgt.SessionStorageEvaluator;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSessionStorageEvaluator;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;

import javax.servlet.Filter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author wuyanshen
 * @Date 2018-11-02 11:51 AM
 * @Discription shiro配置类
 */
@Configuration
public class ShiroConfiguration  extends BaseService {

    /**
     * shiro缓存管理器;
     * 需要注入对应的其它的实体类中：
     * 1、安全管理器：securityManager
     * 可见securityManager是整个shiro的核心；
     * @return
     */
    @Bean
    public EhCacheManager ehCacheManager(CacheManager cacheManager){
        logger.info("-- shiro配置类获取了ehcache的cacheManager");
        EhCacheManager ehCacheManager = new EhCacheManager();
        logger.info("-- shiro将ehcache的cacheManager注入到shiro");
        ehCacheManager.setCacheManager(cacheManager);
        return ehCacheManager;
    }

    /**
     * 禁用session, 不保存用户登录状态。保证每次请求都重新认证。
     * 需要注意的是，如果用户代码里调用Subject.getSession()还是可以用session，如果要完全禁用，要配合下面的noSessionCreation的Filter来实现
     */
    @Bean
    protected SessionStorageEvaluator sessionStorageEvaluator(){
        DefaultWebSessionStorageEvaluator sessionStorageEvaluator = new DefaultWebSessionStorageEvaluator();
        sessionStorageEvaluator.setSessionStorageEnabled(false);
        return sessionStorageEvaluator;
    }


    //springboot freemarker集成shiro标签
    @Bean
    public FreeMarkerConfigurer freeMarkerConfigurer() throws IOException, TemplateException {
        FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer();
        freeMarkerConfigurer.setTemplateLoaderPath("classpath:templates/");
        freemarker.template.Configuration configuration = freeMarkerConfigurer.createConfiguration();
        configuration.setDefaultEncoding("UTF-8");
        //这里可以添加其他共享变量 比如sso登录地址
        configuration.setSharedVariable("shiro", new ShiroTags());
        freeMarkerConfigurer.setConfiguration(configuration);
        return freeMarkerConfigurer;
    }

    //springboot 添加过滤器
    @Bean
    public FilterRegistrationBean registration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new ShiroFormFilter());
//        registration.setEnabled(false);//怎么取消  Filter自动注册,不会添加到FilterChain中.
        return registration;
    }

    //将自己的认证注入spring容器
    @Bean
    public MyShiroRealm myShiroRealm() {
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        return myShiroRealm;
    }

    @Bean
    public JwtRealm jwtRealm() {
        JwtRealm jwtRealm = new JwtRealm();
        return jwtRealm;
    }

    @Bean
    public MyModularRealmAuthenticator modularRealmAuthenticator() {
        MyModularRealmAuthenticator myModularRealmAuthenticator = new MyModularRealmAuthenticator();
        return myModularRealmAuthenticator;
    }

    @Bean
    public SecurityManager securityManager(EhCacheManager ehCacheManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //设置realm.
        securityManager.setAuthenticator(modularRealmAuthenticator());
        securityManager.setRealm(myShiroRealm());//普通表单登录的realm
//        securityManager.setRealm(jwtRealm());//无状态的realm
        securityManager.setCacheManager(ehCacheManager);//设置shiro缓存管理器
        return securityManager;
    }


    //filter工厂，设置对应的过滤条件和跳转条件
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        //登录
        shiroFilterFactoryBean.setLoginUrl("/login");
        //首页
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //错误页面,认证不通过跳转
        shiroFilterFactoryBean.setUnauthorizedUrl("/error/403");


        Map<String, String> map = new HashMap<>();
        /**
         * authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
         */
        map.put("/admin/**", "anon"); //匿名访问静态资源
        map.put("/ztree/**", "anon"); //匿名访问静态资源
        map.put("/treeTable/**", "anon"); //匿名访问静态资源
        map.put("/ckeditor4.10/**", "anon"); //匿名访问静态资源
        map.put("/front/**", "anon"); //匿名访问静态资源
        map.put("/layer/**", "anon"); //匿名访问静态资源
        map.put("/jquery.min.js", "anon"); //匿名访问静态资源
        map.put("/t/**", "anon"); //匿名访问

        //登出
        map.put("/logout", "logout");

        map.put("/api/**", "jwt");//无状态接口,一般是后端提供给前端的restful接口

        //其他资源都拦截
        map.put("/**", "authc");

        Map<String, Filter> mp = new HashMap<>();
        mp.put("authc", new ShiroFormFilter());//form表单形式过滤器
        mp.put("jwt", new JwtFilter());//无状态过滤器
        shiroFilterFactoryBean.setFilters(mp);

        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);

        return shiroFilterFactoryBean;
    }

    //shiro注解开启，不加注解不生效
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    /**
     *  开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)即可实现此功能
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }
}
